﻿//
// Copyright (C) 2008 Eugeny Grishul
//
// See license in License.txt
//

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace NObjectiveASTRegen
{
	internal static class Regen
	{
		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		private static void Main()
		{
			var specials = new[]
			{
				"EOF",
				"Literal",
				"Identifier",
			};

			var specialCharacters = new SortedList<string, string>
			{
				{ "PointerToMemberAccessFromReference", ".*" },
				{ "PointerToMemberAccessFromPointer", "->*" },

				{ "Assign", "=" },
				{ "Plus", "+" },
				{ "Minus", "-" },
				{ "Multiply", "*" },
				{ "Div", "/" },
				{ "Mod", "%" },
						
				{ "Colon", ":" },
				{ "ScopeResolution", "::" },
				{ "Semicolon", ";" },
				{ "Question", "?" },
				{ "Comma", "," },
				{ "Dot", "." },
				{ "Ellipsis", "..." },
					
				{ "OpenCurlyBrace", "{" },
				{ "CloseCurlyBrace", "}" },

				{ "OpenSquareBracket", "[" },
				{ "CloseSquareBracket", "]" },

				{ "OpenParenthesis", "(" },
				{ "CloseParenthesis", ")" },

				{ "GreaterThan", ">" },
				{ "LessThan", "<" },

				{ "Not", "!" },
				{ "LogicalAnd", "&&" },
				{ "LogicalOr", "||" },

				{ "BitwiseComplement", "~" },
				{ "BitwiseAnd", "&" },
				{ "BitwiseOr", "|" },
				{ "Xor", "^" },

				{ "Increment", "++" },
				{ "Decrement", "--" },
				{ "Equality", "==" },
				{ "Inequality", "!=" },
				{ "GreaterThanOrEqual", ">=" },
				{ "LessThanOrEqual", "<=" },
				{ "ShiftLeft", "<<" },
				
				{ "AddAssign", "+=" },
				{ "SubtractAssign", "-=" },
				{ "MultiplyAssign", "*=" },
				{ "DivideAssign", "/=" },
				{ "ModulusAssign", "%=" },
				{ "BitwiseAndAssign", "&=" },
				{ "BitwiseOrAssign", "|=" },
				{ "XorAssign", "^=" },
				{ "ShiftLeftAssign", "<<=" },
				{ "MemberSelection", "->" },

				{ "at_catch", "@catch" },
				{ "at_class", "@class" },
				{ "at_end", "@end" },
				{ "at_finally", "@finally" },
				{ "at_implementation", "@implementation" },
				{ "at_interface", "@interface" },
				{ "at_optional", "@optional" } ,
				{ "at_private", "@private" },
				{ "at_property", "@property" },
				{ "at_protected", "@protected" },
				{ "at_protocol", "@protocol" },
				{ "at_public", "@public" },
				{ "at_required", "@required" },
				{ "at_throw", "@throw" },
				{ "at_try", "@try" },

				{ "_unsigned_char", "unsigned char" },
				{ "_unsigned_short", "unsigned short" },
				{ "_unsigned_int", "unsigned int" },
				{ "_unsigned_long", "unsigned long" },
				{ "_unsigned_long_long", "unsigned long long" },

				{ "_long_double", "long double" },
				{ "_long_long", "long long" },
			};

			using( _lexerCode = new StreamWriter( "..\\..\\Lexer.Generated.cs" ) )
			{
				_lexerCode.WriteLine( "//" );
				_lexerCode.WriteLine( "// Copyright (C) 2008 Eugeny Grishul" );
				_lexerCode.WriteLine( "//" );
				_lexerCode.WriteLine( "// See license in License.txt" );
				_lexerCode.WriteLine( "//" );
				_lexerCode.WriteLine( "// This file is generated by RegenLexer project" );
				_lexerCode.WriteLine( "//" );
				_lexerCode.WriteLine();

				_lexerCode.WriteLine( "using System;" );
				_lexerCode.WriteLine( "using System.Collections.Generic;" );
				_lexerCode.WriteLine();
				_lexerCode.WriteLine( "namespace NObjectiveAST" );
				_lexerCode.WriteLine( "{" );
				_lexerCode.WriteLine( "\tpublic partial class Lexer" );
				_lexerCode.WriteLine( "\t{" );

				_lexerCode.WriteLine( "\t\tpublic enum Tokens" );
				_lexerCode.WriteLine( "\t\t{" );

				foreach( var item in specials )
				{
					_lexerCode.Write( "\t\t\t" );
					_lexerCode.Write( item );
					_lexerCode.WriteLine( ',' );
				}

				foreach( var item in specialCharacters )
				{
					_lexerCode.Write( "\t\t\t" );
					_lexerCode.Write( item.Key );
					_lexerCode.WriteLine( ',' );
				}

				foreach( var item in NObjectiveAST.Lexer.Keywords )
				{
					_lexerCode.Write( "\t\t\t" );
					_lexerCode.Write( ( item[0] == '@' ? "at_" : "_" + item[0] ) + item.Substring( 1 ).Replace( ' ', '_' ) );
					_lexerCode.WriteLine( ',' );
				}

				_lexerCode.WriteLine( "\t\t}" );
				_lexerCode.WriteLine();

				// hex value symbol table
				_lexerCode.Write( "\t\tprivate static readonly int[] HexValue = new[] { " );
				for( int i = 0; i < 256; i++ )
				{
					if( char.IsDigit( ( char ) i ) )
					{
						_lexerCode.Write( "{0}, ", i - '0' );
						continue;
					}

					var letter = char.ToLower( ( char ) i );

					if( letter >= 'a' && letter <= 'f' )
					{
						_lexerCode.Write( "{0}, ", letter - 'a' + 10 );
						continue;
					}

					_lexerCode.Write( "-1, " );
				}
				_lexerCode.WriteLine( "};" );

				// identifier symbol table
				_lexerCode.Write( "\t\tprivate static readonly bool[] IsIdentifierCharacter = new[] { " );
				for( int i = 0; i < 256; i++ )
				{
					if( char.IsLetterOrDigit( ( char ) i ) || ( char ) i == '_' || ( char ) i == '$' )
						_lexerCode.Write( "true, " );
					else
						_lexerCode.Write( "false, " );
				}
				_lexerCode.WriteLine( "};" );

				// from which sybols identifier can be started
				_lexerCode.Write( "\t\tprivate static readonly bool[] IsIdentifierStartCharacter = new[] { " );
				for( int i = 0; i < 256; i++ )
				{
					if( char.IsLetter( ( char ) i ) || ( char ) i == '_' || ( char ) i == '$' )
						_lexerCode.Write( "true, " );
					else
						_lexerCode.Write( "false, " );
				}
				_lexerCode.WriteLine( "};" );
				_lexerCode.WriteLine();

				_lexerCode.WriteLine( "\t\tpublic static string GetTokenString( Tokens token )" );
				_lexerCode.WriteLine( "\t\t{" );
				_lexerCode.WriteLine( "\t\t\tswitch( token )" );
				_lexerCode.WriteLine( "\t\t\t{" );

				foreach( var item in specialCharacters )
					_lexerCode.WriteLine( "\t\t\t\tcase Tokens.{0}: return \"{1}\";", item.Key, item.Value );

				foreach( var item in NObjectiveAST.Lexer.Keywords )
					_lexerCode.WriteLine( "\t\t\t\tcase Tokens.{0}: return \"{1}\";", ( item[0] == '@' ? "at_" : "_" + item[0] ) + item.Substring( 1 ).Replace( ' ', '_' ), item );

				_lexerCode.WriteLine( "\t\t\t}" );
				_lexerCode.WriteLine( "\t\t\tthrow new ArgumentException( \"token\" );" );
				_lexerCode.WriteLine( "\t\t}" );

				Indent += 2;
				_lexerCode.WriteLine();
				_lexerCode.WriteLine( "private void TokenizeWord()" );
				Indent++;
				_lexerCode.WriteLine( "{" );

				WriteKeywordChoice( NObjectiveAST.Lexer.Keywords, "", 0, null );

				//Indent--;
				//_lexerCode.WriteLine();
				_lexerCode.WriteLine( "default_recognizer:" );
				_lexerCode.WriteLine( "_tokens[_writeIndex].Type = Tokens.Identifier;" );
				_lexerCode.WriteLine( "_tokens[_writeIndex].Value = ReadIdentifier();" );
				_lexerCode.WriteLine( "_tokens[_writeIndex].Row = CurrentRow;" );
				_lexerCode.WriteLine( "_tokens[_writeIndex].Column = CurrentColumn;" );
				_lexerCode.WriteLine( "_writeIndex++;" );
				Indent--;
				_lexerCode.WriteLine();
				_lexerCode.WriteLine( "}" );
				Indent--;
				Indent--;

				Indent += 2;
				_lexerCode.WriteLine();
				_lexerCode.WriteLine( "private void TokenizeObjectiveWord()" );
				Indent++;
				_lexerCode.WriteLine( "{" );

				WriteKeywordChoice( NObjectiveAST.Lexer.ObjectiveKeywords, "at", 0, null );

				Indent--;
				_lexerCode.WriteLine( "default_recognizer:" );
				_lexerCode.WriteLine( @"ThrowException( ""Undefined objective-c keyword: {0}"", ReadIdentifier() );" );
				Indent--;
				_lexerCode.WriteLine( "}" );
				Indent--;

				_lexerCode.WriteLine( "}" );
				_lexerCode.WriteLine( "}" );
			}
		}

		private static void WriteKeywordChoice( string[] keywords, string tokenPrefix, int level, string startsWith )
		{
			var alternatives = keywords.Where( x => x.Length > level && ( startsWith != null ? x.StartsWith( startsWith ) : true ) ).GroupBy( x => x[level] );

			if( keywords.Contains( startsWith ) )
			{
				_lexerCode.WriteLine( "if( _readIndex + {0} >=_text.Length || !IsIdentifierCharacter[( byte ) _text[_readIndex + {0}]] )", level );
				Indent++;
				_lexerCode.WriteLine( "{" );

				if( startsWith == "char" || startsWith == "short" || startsWith == "int" || startsWith == "long" )
				{
					_lexerCode.WriteLine( "if( _writeIndex >= 1 && _tokens[_writeIndex - 1].Type == Tokens._signed ) _writeIndex--;" );
					_lexerCode.WriteLine( "if( _writeIndex >= 1 && _tokens[_writeIndex - 1].Type == Tokens._unsigned )" );
					Indent++;
					_lexerCode.WriteLine( "{" );

					_lexerCode.WriteLine( "_tokens[_writeIndex - 1].Type = Tokens.{1}_unsigned_{0};", startsWith, tokenPrefix );
					_lexerCode.WriteLine( "_readIndex += {0};", level );
					_lexerCode.WriteLine( "return;" );

					Indent--;
					_lexerCode.WriteLine();
					_lexerCode.WriteLine( "}" );
				}

				if( startsWith == "int" )
				{
					_lexerCode.WriteLine( "if( _writeIndex >= 1 && ( _tokens[_writeIndex - 1].Type == Tokens._long || _tokens[_writeIndex - 1].Type == Tokens._unsigned_long || _tokens[_writeIndex - 1].Type == Tokens._long_long || _tokens[_writeIndex - 1].Type == Tokens._unsigned_long_long ) )" );
					Indent++;
					_lexerCode.WriteLine( "{" );

					_lexerCode.WriteLine( "_readIndex += {0};", level );
					_lexerCode.WriteLine( "return;" );

					Indent--;
					_lexerCode.WriteLine();
					_lexerCode.WriteLine( "}" );
				}

				if( startsWith == "unsigned" )
				{
					_lexerCode.WriteLine( "if( _writeIndex >= 1 && _tokens[_writeIndex - 1].Type == Tokens._char ) {{ _tokens[_writeIndex - 1].Type = Tokens._unsigned_char; _readIndex += {0}; return; }}", level );
					_lexerCode.WriteLine( "if( _writeIndex >= 1 && _tokens[_writeIndex - 1].Type == Tokens._short ) {{ _tokens[_writeIndex - 1].Type = Tokens._unsigned_short; _readIndex += {0}; return; }}", level );
					_lexerCode.WriteLine( "if( _writeIndex >= 1 && _tokens[_writeIndex - 1].Type == Tokens._int ) {{ _tokens[_writeIndex - 1].Type = Tokens._unsigned_int; _readIndex += {0}; return; }}", level );
					_lexerCode.WriteLine( "if( _writeIndex >= 1 && _tokens[_writeIndex - 1].Type == Tokens._long ) {{ _tokens[_writeIndex - 1].Type = Tokens._unsigned_long; _readIndex += {0}; return; }}", level );
					_lexerCode.WriteLine( "if( _writeIndex >= 1 && _tokens[_writeIndex - 1].Type == Tokens._long_long ) {{ _tokens[_writeIndex - 1].Type = Tokens._unsigned_long_long; _readIndex += {0}; return; }}", level );
				}

				if( startsWith == "long" )
				{
					_lexerCode.WriteLine( "if( _writeIndex >= 1 && _tokens[_writeIndex - 1].Type == Tokens._long )" );
					Indent++;
					_lexerCode.WriteLine( "{" );

					_lexerCode.WriteLine( "_writeIndex--;" );
					_lexerCode.WriteLine( "NewToken( Tokens.{1}_long_{0} );", startsWith, tokenPrefix );
					_lexerCode.WriteLine( "_readIndex += {0};", level );
					_lexerCode.WriteLine( "return;" );

					Indent--;
					_lexerCode.WriteLine();
					_lexerCode.WriteLine( "}" );

					_lexerCode.WriteLine( "if( _writeIndex >= 1 && _tokens[_writeIndex - 1].Type == Tokens._unsigned_long )" );
					Indent++;
					_lexerCode.WriteLine( "{" );

					_lexerCode.WriteLine( "_writeIndex--;" );
					_lexerCode.WriteLine( "NewToken( Tokens.{1}_unsigned_long_{0} );", startsWith, tokenPrefix );
					_lexerCode.WriteLine( "_readIndex += {0};", level );
					_lexerCode.WriteLine( "return;" );

					Indent--;
					_lexerCode.WriteLine();
					_lexerCode.WriteLine( "}" );
				}

				if( startsWith == "double" )
				{
					_lexerCode.WriteLine( "if( _writeIndex >= 1 && _tokens[_writeIndex - 1].Type == Tokens._long )" );
					Indent++;
					_lexerCode.WriteLine( "{" );

					_lexerCode.WriteLine( "_writeIndex--;" );
					_lexerCode.WriteLine( "NewToken( Tokens.{1}_long_{0} );", startsWith, tokenPrefix );
					_lexerCode.WriteLine( "_readIndex += {0};", level );
					_lexerCode.WriteLine( "return;" );

					Indent--;
					_lexerCode.WriteLine();
					_lexerCode.WriteLine( "}" );
				}

				_lexerCode.WriteLine( "NewToken( Tokens.{1}_{0} );", startsWith, tokenPrefix );
				_lexerCode.WriteLine( "_readIndex += {0};", level );
				_lexerCode.WriteLine( "return;" );

				Indent--;
				_lexerCode.WriteLine();

				_lexerCode.WriteLine( "}" );
			}

			_lexerCode.WriteLine( "if( _readIndex + {0} >= _text.Length )", level );
			_lexerCode.WriteLine( "\tgoto default_recognizer;" );

			if( alternatives.Count() > 0 )
			{
				_lexerCode.WriteLine( "switch( ( byte ) _text[_readIndex + {0}] )", level );
				Indent++;
				_lexerCode.WriteLine( "{" );

				foreach( var item in alternatives )
				{
					_lexerCode.WriteLine( "case ( byte ) '{0}':", item.Key );
					WriteKeywordChoice( keywords, tokenPrefix, level + 1, startsWith + item.Key );

					_lexerCode.WriteLine( "break;" );
				}

				Indent--;
				_lexerCode.WriteLine();
				_lexerCode.WriteLine( "}" );
			}
		}

		static StreamWriter _lexerCode;

		static int _indent = 0;
		static int Indent
		{
			get { return _indent; }
			set
			{
				_indent = value;
				_lexerCode.NewLine = Environment.NewLine + IndentString;
			}
		}

		static string IndentString { get { return new string( '\t', _indent ); } }
	}

}
